package qc.module.demo.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import qc.common.core.exception.QCPromptException;
import qc.common.core.pair.GenericKeyValuePairDto;
import qc.common.core.unify.QCUnifyReturnValue;
import qc.common.core.utils.DateUtil;
import qc.common.core.utils.TimeSpanUtil;
import qc.module.demo.dto.tsnumdata.TSNumDataQueryResultDto;
import qc.module.demo.dto.tsnumdata.TsNumDataDto;
import qc.module.demo.dto.tsnumdata.TsNumDataQueryConditionDto;
import qc.module.demo.entity.TsNumData;
import qc.module.demo.entity.TsPoints;
import qc.module.demo.mapper.TsNumDataMapper;
import qc.module.demo.mapper.TsPointsMapper;
import qc.module.demo.repository.TsNumDataRepository;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;

/**
 * 时序数值数据 TsNumDataService
 * @author YRR
 * @create 2025-04-11 15:05
 */
@Service
public class TsNumDataService {

    @Autowired
    private TsNumDataRepository repository;

    @Autowired
    private TsPointsService pointsService;

    /**
     * 根据条件查询数值的数据
     *
     * @param condition 查询条件
     * @return List<TsNumDataDto>
     */
    public TSNumDataQueryResultDto query(TsNumDataQueryConditionDto condition) throws QCPromptException {
        //1.查询条件有效性验证
        if (condition == null) throw new QCPromptException("查询条件不能为空");
        if (condition.getPointids() == null) throw new QCPromptException("查询时序数值的点号不能为空");
        if (StringUtils.isBlank(condition.getBegintm())) throw new QCPromptException("查询时序数值的起始时间不能为空");
        if (StringUtils.isBlank(condition.getEndtm())) throw new QCPromptException("查询时序数值的截至时间不能为空");

        //2.查询时间处理
        Date beginTm = null;
        Date endTm = null;
        if (StringUtils.isNotBlank(condition.getBegintm())) {
            beginTm = DateUtil.parseDate(condition.getBegintm());
            if (beginTm == null) {
                throw new QCPromptException("查询时序数值的开始日期转换错误");
            }
        }
        if (StringUtils.isNotBlank(condition.getEndtm())) {
            endTm = DateUtil.parseDate(condition.getEndtm());
            if (endTm == null) {
                throw new QCPromptException("查询时序数值的截止日期转换错误");
            }
        }

        //3、校验时间符合规范，结束时间大于开始时间
        if (beginTm.after(endTm)) {
            throw new QCPromptException("查询时序数值的开始日期不能大于截止日期");
        }

        //4.一次性查询所有点号信息
        List<TsPoints> points = pointsService.getEntities(condition.getPointids());
        if (points == null || points.size() < 0x0) {
            throw new QCPromptException("查询时序数值的点号不存在");
        }

        TSNumDataQueryResultDto result = new TSNumDataQueryResultDto();
        //对返回结果中的点号集合进行赋值
        result.setPoints(TsPointsMapper.MAPPER.toDtoList(points));

        //5、创建查询条件
        LambdaQueryWrapper<TsNumData> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(TsNumData::getPointid, condition.getPointids())
                .ge(TsNumData::getTm, beginTm)
                .le(TsNumData::getTm, endTm)
                .orderByAsc(TsNumData::getTm);// 按时间升序排序

        //7.创建查询结果
        List<TsNumData> list = repository.selectList(wrapper);
        if (list != null && list.size() > 0x0) {
            List<TsNumDataDto> datas = new ArrayList<>();

            for (TsNumData tsNumData : list) {
                TsNumDataDto tsNumDataDto = TsNumDataMapper.MAPPER.toDto(tsNumData);
                //根据点号ID获取点号信息，根据点号信息中的时段类型对数据时标进行格式化输出
                Optional<TsPoints> point = points.stream().filter(p -> StringUtils.equalsIgnoreCase(tsNumData.getPointid(), p.getPointid())).findFirst();
                if (point != null && point.isPresent()) {
                    tsNumDataDto.setTmstr(DateUtil.getFormatString(tsNumData.getTm(), point.get().getTmtype()));
                }

                datas.add(tsNumDataDto);
            }

            result.setDatas(datas);
        }

        return result;
    }

    /***
     * 查询指定时间点的点号数值数据集合
     *
     * @param pointids 点号集合
     * @param tm 指定的时间点
     * @return java.util.List<qc.module.demo.dto.tsnumdata.TsNumDataDto>
     * @author QuCheng Tech
     * @since 2025/5/19
     */
    public TSNumDataQueryResultDto queryFixed(String[] pointids, Date tm) throws QCPromptException {
        TSNumDataQueryResultDto result = new TSNumDataQueryResultDto();

        if (pointids != null && pointids.length > 0x0) {
            List<TsPoints> points = pointsService.getEntities(pointids);//使用查询点号集合方法，对点号进行排序
            if (points != null && points.size() > 0x0) {
                //对返回结果中的点号集合进行赋值
                result.setPoints(TsPointsMapper.MAPPER.toDtoList(points));

                List<TsNumDataDto> datas = new ArrayList<>();
                for (TsPoints point : points) {
                    //根据点号的时段类型处理指定的时间点
                    Date pointQueryFixedTm = TimeSpanUtil.getBeginTime(tm, point.getTmtype());

                    //初始化返回结果中的当前点号信息
                    TsNumDataDto resultItem = new TsNumDataDto();
                    resultItem.setPointid(point.getPointid());
                    resultItem.setTm(DateUtil.getDateTimeString(pointQueryFixedTm));
                    resultItem.setTmstr(DateUtil.getFormatString(pointQueryFixedTm, point.getTmtype()));
                    //初始化数据值为NaN表示为空
                    resultItem.setValue(Double.NaN);

                    //查询指定时间点的点号数据值
                    TsNumData numData = this.get(point.getPointid(), pointQueryFixedTm);
                    if (numData != null) {
                        resultItem.setValue(numData.getValue().doubleValue());
                        resultItem.setIntm(DateUtil.getDateTimeString(numData.getIntm()));
                    }

                    datas.add(resultItem);
                }

                result.setDatas(datas);
            }
        }

        return result;
    }

    /**
     * 批量保存时序数值数据
     *
     * @param dtoList
     * @return 返回null表示全部成功，否则返回失败信息
     */
    public String save(List<TsNumDataDto> dtoList) {
        //1.校验参数
        if (CollectionUtils.isEmpty(dtoList)) return QCUnifyReturnValue.Warn("保存时序数值数据集合不能为空");

        //需要自动根据点号的时段类型对传入的数据时标进行规整处理，因此需要先查询点号集合信息
        String[] distinctPointIds = dtoList.stream().filter(p -> p != null && StringUtils.isNotBlank(p.getPointid())).map(p -> p.getPointid()).distinct().toArray(String[]::new);
        List<TsPoints> points = pointsService.getEntities(distinctPointIds);

        //2.逐条验证并保存，统计结果
        int successCount = 0x0;
        int failCount = 0x0;

        //3.遍历检查每条数据并转换
        for (TsNumDataDto dto : dtoList) {
            //3.1.检查每条数据
            if (dto == null || StringUtils.isBlank(dto.getPointid())) {
                failCount++;
                continue;
            }

            //3.2.时标值判断
            if (StringUtils.isBlank(dto.getTm())) {
                failCount++;
                continue;
            }

            //3.3.值判断
            if (dto.getValue() < 0x0) {
                failCount++;
                continue;
            }

            //3.4.时间类型判断
            Date tm = DateUtil.parseDate(dto.getTm());
            if (tm == null) {
                failCount++;
                continue;
            }

            //获取点号信息中的时段长度类型，根据时段长度类型规整传入的数据时标
            String pointId = dto.getPointid();
            Optional<TsPoints> point = points.stream().filter(p -> StringUtils.equalsIgnoreCase(dto.getPointid(), p.getPointid())).findFirst();
            if (point != null && point.isPresent()) {
                try {
                    tm = TimeSpanUtil.getBeginTime(tm, point.get().getTmtype());
                } catch (QCPromptException e) {
                    e.printStackTrace();
                }
            }

            //3.5.检查记录是否存在，决定是执行更新还是新增
            boolean exist = hasExist(pointId, tm);

            //3.6.将dto转换成entity,并添加
            TsNumData en = TsNumDataMapper.MAPPER.toEntity(dto);
            en.setTm(tm);
            en.setIntm(DateUtil.getNowDate());

            //3.7.判断是新增还是修改
            if (exist) {
                //构建查询条件
                LambdaQueryWrapper<TsNumData> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(TsNumData::getPointid, pointId)
                        .eq(TsNumData::getTm, tm);

                //执行更新
                if (repository.update(en, wrapper) > 0x0) {
                    //更新成功计数
                    successCount++;
                } else {
                    failCount++;
                }

            } else {
                //记录不存在，执行新增
                if (repository.insert(en) > 0x0) {
                    successCount++;
                } else {
                    failCount++;
                }
            }
        }

        //4.返回结果
        if (failCount > 0x0 && successCount > 0x0) {
            return QCUnifyReturnValue.Warn("时序数值数据保存成功了" + successCount + "条，失败了" + failCount + "条");
        } else if (successCount < 0x1) {
            return QCUnifyReturnValue.Warn("时序数值数据保存全部失败");
        }
        return null;
    }

    /**
     * 判断指定的点号和时间点数据是否存在，只判断数据记录是否有，用于判断数据保存时新增还是修改
     *
     * @param pointid 时序数据点号的点号
     * @param tm      时序数据点号的时标
     * @return true表示存在，false表示不存在
     */
    private boolean hasExist(String pointid, Date tm) {
        return get(pointid, tm) != null;
    }

    /***
     * 获取指定点号和指定时间点的数据，不存在返回null
     *
     * @param pointId 指定的点号
     * @param tm 指定的时间点
     * @return qc.module.demo.entity.TsNumData
     * @author QuCheng Tech
     * @since 2025/5/19
     */
    public TsNumData get(String pointId, Date tm) {
        if (StringUtils.isNotBlank(pointId)) {
            LambdaQueryWrapper<TsNumData> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(TsNumData::getPointid, pointId)
                    .eq(TsNumData::getTm, tm);

            //如果有多条按写入时间降序获取第一条
            wrapper.orderByDesc(TsNumData::getIntm);

            return repository.selectOne(wrapper);
        }

        return null;
    }

    /**
     * 根据点号和时间批量删除数据
     *
     * @param dtoList deleteList 删除条件列表
     * @return 返回null表示成功，否则返回错误信息
     */
    public String delete(List<TsNumDataDto> dtoList) {
        //1.参数校验
        if (CollectionUtils.isEmpty(dtoList)) return QCUnifyReturnValue.Warn("删除时序数值的条件不能为空");

        //2.统计成功和失败数量
        int successCount = 0;
        int failCount = 0;

        //3.逐条验证参数
        for (TsNumDataDto dto : dtoList) {
            //3.1.校验点号
            if (dto == null || StringUtils.isBlank(dto.getPointid())) {
                failCount++;
                continue;
            }

            //3.2.校验时标
            if (StringUtils.isBlank(dto.getTm())) {
                failCount++;
                continue;
            }

            //3.3.校验时间类型
            Date tm = DateUtil.parseDate(dto.getTm());
            if (tm == null) {
                failCount++;
                continue;
            }

            String pointId = dto.getPointid();
            //3.4.验证复合组件是否存在来判断删除
            if (hasExist(pointId, tm)) {
                LambdaQueryWrapper<TsNumData> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(TsNumData::getPointid, pointId)
                        .eq(TsNumData::getTm, tm);

                // 3.5.执行删除操作并统计结果
                if (repository.delete(wrapper) > 0) {
                    successCount++;
                } else {
                    failCount++;
                }
            } else {
                //记录不存在，计入失败数
                failCount++;
            }
        }

        //4.返回删除结果
        if (failCount > 0 && successCount > 0) {
            return QCUnifyReturnValue.Warn("时序数值数据删除成功" + successCount + "条，失败" + failCount + "条");
        } else if (successCount < 0x1) {
            return QCUnifyReturnValue.Warn("时序数值数据删除全部失败");
        }
        return null;
    }

    /**
     * 根据点号和时间查询数据
     * @param pointId   点号
     * @param tm        时标
     * @param isFixTime 是否固定只查询指定的时间点
     * @return GenericKeyValuePairDto<Date, Double> 返回查询结果
     */
    public GenericKeyValuePairDto<Date, Double> getByPointIdAndTm(String pointId, Date tm, boolean isFixTime) {
        try {
            LambdaQueryWrapper<TsNumData> wrapper = new LambdaQueryWrapper<>();
            if (isFixTime) {
                // 精确查询，计划
                wrapper.eq(TsNumData::getPointid, pointId)
                        .eq(TsNumData::getTm, tm);
            } else {
                // 查询煤，获取小于等于指定日期的最新记录
                wrapper.eq(TsNumData::getPointid, pointId)
                        .le(TsNumData::getTm, tm)
                        .orderByDesc(TsNumData::getTm)
                        .last("limit 1");
            }

            // 查询数据库
            TsNumData en = repository.selectOne(wrapper);

            //直接构建并返回GenerKeyValuePoirDto对象
            if (en != null) {
                GenericKeyValuePairDto<Date, Double> result = new GenericKeyValuePairDto<>();
                result.setKey(en.getTm());
                result.setValue(en.getValue());
                return result;
            }
        } catch (Exception e) {
            //异常时捕获，不进行处理，返回null
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 查询指定点号起止时间内的数据，包含起止时间点
     * @param pointId
     * @param beginTime
     * @param endTime
     * @return java.util.List<qc.common.core.pair.GenericKeyValuePairDto < java.util.Date, java.lang.Double>>
     * @author QuCheng Tech
     * @since 2025/4/22
     */
    public List<GenericKeyValuePairDto<Date, Double>> query(String pointId, Date beginTime, Date endTime) {
        try {
            LambdaQueryWrapper<TsNumData> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(TsNumData::getPointid, pointId)
                    .ge(TsNumData::getTm, beginTime)
                    .le(TsNumData::getTm, endTime)
                    .orderByAsc(TsNumData::getTm);

            // 查询数据库
            List<TsNumData> ens = repository.selectList(wrapper);
            if (ens != null && ens.size() > 0x0) {
                List<GenericKeyValuePairDto<Date, Double>> result = new ArrayList<>();

                for (TsNumData en : ens) {
                    GenericKeyValuePairDto<Date, Double> kv = new GenericKeyValuePairDto<>();
                    kv.setKey(en.getTm());
                    kv.setValue(en.getValue());

                    result.add(kv);
                }
                return result;
            }
        } catch (Exception e) {
            //异常时捕获，不进行处理，返回null
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 修改点号数值数据
     *
     * @param dto 修改条件列表
     * @return 返回null表示成功，否则返回错误信息
     */
    public String update(TsNumDataDto dto) {
        //1.参数校验
        if (dto == null) return QCUnifyReturnValue.Warn("修改时序数值的条件不能为空");
        if (StringUtils.isBlank(dto.getPointid()))
            return QCUnifyReturnValue.Warn("修改时序数值的点号不能为空");

        if (StringUtils.isBlank(dto.getTm())) return QCUnifyReturnValue.Warn("修改时序数值的时标不能为空");
        Date now = DateUtil.getNowDate();

        //2.验证日期
        Date tm = DateUtil.parseDate(dto.getTm());
        if (tm == null) return QCUnifyReturnValue.Warn("修改时序数值的时标时间格式不正确");
        //数据时间根据点号的时段长度类型进行规整
        String pointId = dto.getPointid();
        TsPoints point = pointsService.getEntity(pointId);
        if (point == null)
            return QCUnifyReturnValue.Warn("修改时序数值的点号信息不存在");
        try {
            tm = TimeSpanUtil.getBeginTime(tm, point.getTmtype());
        } catch (QCPromptException e) {
            e.printStackTrace();
        }

        //3.判断修改的主键是否存在
        if (!hasExist(pointId, tm)) {
            return QCUnifyReturnValue.Warn("修改时序数值的主键不存在");
        }

        //4.修改数据
        TsNumData en = TsNumDataMapper.MAPPER.toEntity(dto);
        en.setIntm(now);
        en.setTm(tm);

        //5.修改条件
        LambdaUpdateWrapper<TsNumData> wrapper = new LambdaUpdateWrapper<>();

        wrapper.eq(TsNumData::getPointid, pointId)
                .eq(TsNumData::getTm, tm)
                .set(TsNumData::getValue, dto.getValue())
                .set(TsNumData::getIntm, now);

        if (repository.update(null, wrapper) < 0x1) {
            return QCUnifyReturnValue.Warn("时序数值的数据修改失败");
        }
        return QCUnifyReturnValue.Success();
    }

    /**
     * 新增点号数值数据1条
     *
     * @param dto
     * @return
     */
    public String add(TsNumDataDto dto) {
        //1.检验参数
        if (dto == null) return QCUnifyReturnValue.Warn("时序数据的增加条件不能为空");
        if (StringUtils.isBlank(dto.getPointid()))
            return QCUnifyReturnValue.Warn("新增时序数值点号不能为空");
        if (StringUtils.isBlank(dto.getTm())) return QCUnifyReturnValue.Warn("新增时序数值的时标不能为空");

        Date now = DateUtil.getNowDate();

        //2.验证日期
        Date tm = DateUtil.parseDate(dto.getTm());
        if (tm == null) return QCUnifyReturnValue.Warn("新增时序数值的时标时间格式不正确");

        //3.判断点号是否存在点号表中
        //数据时间根据点号的时段长度类型进行规整
        String pointId = dto.getPointid();
        TsPoints point = pointsService.getEntity(pointId);
        if (point == null)
            return QCUnifyReturnValue.Warn("新增时序数值的点号信息不存在");
        try {
            tm = TimeSpanUtil.getBeginTime(tm, point.getTmtype());
        } catch (QCPromptException e) {
            e.printStackTrace();
        }

        //4.判断时序数值表的主键是否存在
        if (hasExist(pointId, tm)) {
            return QCUnifyReturnValue.Warn("相同点号和时的数据已经存在");
        }

        //5.新增数据
        TsNumData en = TsNumDataMapper.MAPPER.toEntity(dto);
        en.setTm(tm);
        en.setIntm(now);

        if (repository.insert(en) < 0x1) {
            return QCUnifyReturnValue.Warn("时序数值的数据新增失败");
        }
        return QCUnifyReturnValue.Success();
    }
}
